﻿using Azure;
using DotNetCommon.Extensions;
using LiveCharts;
using LiveCharts.Wpf;
using log4net;
using Org.BouncyCastle.Pqc.Crypto.Lms;
using Sunny.UI;
using System.Collections;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Mail;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using TencentCloud.Common;
using TencentCloud.Common.Profile;
using TencentCloud.Sms.V20210111;
using TencentCloud.Sms.V20210111.Models;
using Excel = Microsoft.Office.Interop.Excel;
using File = System.IO.File;
using MessageBox = System.Windows.Forms.MessageBox;

enum SENSOR_TYPE : int
{
    NORMAL_SENSOR = 0, STRAIN_SENSOR, TEMPER_SENSOR, DISPLACE_SENSOR
};

namespace FZRD
{
    public partial class Page1 : UIPage
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(Page1));

        //监听线程
        [DisallowNull]
        public static Thread receiveThread;
        //服务器套接字
        public static Socket serverSocket;
        //服务器端口
        public static EndPoint adEndPoint;
        //是否打开了服务端口
        public static bool isOpenServerPort;

        public double waveBeginValue;

        public int waveInterval;

        public int waveDataPoints;

        public int waveChannelCount;

        public int waveRetDataRate;

        bool debug = true;
        //X轴值数量初始值
        private long xCount = 120;
        //X轴值最大计数值
        //private long xTimes = 1000;
        //显示时长
        private long showTimes = 60;//(秒)
        //当前时间
        private long lastTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
        //采样频率
        private long frequency = 20;
        //采集间隔
        private long intervalTime = 500;//(毫秒)s
        //通道初始值
        private int channelIndex = 0;
        //通道初始值
        private int rasterIndex = 0;
        //光栅总数
        private int rasterCount = 30;
        //通道总数
        private int channelCount = 16;
        private Random R { get; set; }//为了给Y值提供随机值
        //定时刷新曲线
        private System.Windows.Forms.Timer timerChart;

        //普通传感器 0
        //应变传感器 1
        //倾角传感器 2
        //加速度传感器 3
        //温度传感器 4
        //位移传感器 5
        //应力传感器 6
        //压力传感器 7
        //震动传感器 8
        //扭矩传感器 9
        //沉降传感器 10
        private int sensorType = 0;

        private bool isSaveWaveLength = false;

        private bool isSaveDataToMysql = false;
        private bool isSaveDataToExcel = false;
        private bool isSaveDataToTxt = false;

        private string waveFilePath = "D:\\聚芯光纤\\";

        private string waveFileName = "波长数据";

        private string waveFileNameDate = "";

        [DisallowNull]
        private Excel.Application excelApp;

        [DisallowNull]
        private Excel._Workbook workbook;

        //[DisallowNull]
        //private FileStream fs;

        [DisallowNull]
        private StreamWriter sw;

        private string filePath;

        private bool isWaveCollectTimes = false;

        private int waveCollectTimes = 0;

        private List<ColumnData> dataList = new List<ColumnData>();

        //轴力超过阈值次数
        private static int axialAlarmRepeatTimes = 0;

        private static bool isAlarmNotify = true;
        private static double axialForceExceeds = 8000.0;
        private static int repeatTimes = 100;

        private static bool isAlarmEmail = true;
        private static string alarmEmail = "";

        private static bool isAlarmPhone = true;
        private static string alarmPhone = "";

        //温度传感器初始光栅波长
        private static double tempStartWave = 1550.110;
        //温度传感器初始温度
        private static double tempStartValue = 25;

        private Dictionary<int, Dictionary<int, SensorEntity>> entityList = new Dictionary<int, Dictionary<int, SensorEntity>>();

        private string rasterTableName = "raster_wave_normal";
        private string sensorTableName = "sensor_device_data";
        private int timeoutSeconds = 60 * 30;
        private int notifyAfter = 0;
        private Func<long, Task<bool>> callBack;
        private CancellationToken cancellationToken = default;
        // TCP服务端监听
        private TcpListener tcpServer = null;
        // 客户端连接初始化
        //public static List<string> dictInfo = new List<string>();
        //连接的客户端列表
        public static List<TcpClient> tcpClientList = new List<TcpClient>();
        //连接的客户端监听列表
        public static List<Thread> tcpClientThreads = new List<Thread>();

        public Page1()
        {
            InitializeComponent();

            InitIntfaceData();

            InitChartConfig();

            #region  多Y轴曲线
            //将自定义类型MeasureModel映射到曲线坐标点
            var mapper = LiveCharts.Configurations.Mappers.Xy<DateTimePoint>()
              .X(model => model.Time)    //使用 Time.作为X轴的标签
              .Y(model => model.Value);  //使用Value 作为Y轴的值

            //全局保存映射器
            Charting.For<DateTimePoint>(mapper);
            #endregion

            this.cbWaveChannelSellect.SelectedIndex = Convert.ToInt32(ConfigurationManager.AppSettings["cbWaveChannelSellect"]);

            this.cbWaveSensorSellect.SelectedIndex = Convert.ToInt32(ConfigurationManager.AppSettings["cbWaveSensorSellect"]);

            this.cbWaveSensorType.SelectedIndex = Convert.ToInt32(ConfigurationManager.AppSettings["cbWaveSensorType"]);

            this.tbWaveFrequency.Text = ConfigurationManager.AppSettings["tbWaveFrequency"];

            this.cbWaveCollectTimes.Checked = Convert.ToBoolean(ConfigurationManager.AppSettings["cbWaveCollectTimes"]);

            this.tbWaveCollectTimes.Text = ConfigurationManager.AppSettings["tbWaveCollectTimes"];

            this.cbSaveWaveExcel.Checked = Convert.ToBoolean(ConfigurationManager.AppSettings["cbSaveWaveExcel"]);

            this.waveFilePath = ConfigurationManager.AppSettings["tbWaveFilePath"];

            this.waveFileName = ConfigurationManager.AppSettings["WaveFileName"];

            this.tbWaveFilePath.Text = this.waveFilePath;

            this.isSaveDataToMysql = Convert.ToBoolean(ConfigurationManager.AppSettings["isSaveDataToMysql"]);

            this.isSaveDataToExcel = Convert.ToBoolean(ConfigurationManager.AppSettings["isSaveDataToExcel"]);

            this.isSaveDataToTxt = Convert.ToBoolean(ConfigurationManager.AppSettings["isSaveDataToTxt"]);

            isAlarmNotify = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmNotify"]);

            axialForceExceeds = Convert.ToDouble(ConfigurationManager.AppSettings["tbAxialForceExceeds"]);

            repeatTimes = Convert.ToInt16(ConfigurationManager.AppSettings["tbRepeatTimes"]);

            isAlarmPhone = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmPhone"]);

            alarmPhone = ConfigurationManager.AppSettings["tbAlarmPhone"];

            isAlarmEmail = Convert.ToBoolean(ConfigurationManager.AppSettings["cbAlarmEmail"]);

            alarmEmail = ConfigurationManager.AppSettings["tbAlarmEmail"];

            tempStartWave = Convert.ToDouble(ConfigurationManager.AppSettings["tempStartWave"]);

            tempStartValue = Convert.ToDouble(ConfigurationManager.AppSettings["tempStartValue"]);

            CreateExcelDataFile();

            if (this.tbWaveFrequency.Text.Length > 0)
            {
                long _frequency = Convert.ToInt64(this.tbWaveFrequency.Text);
                //转换成长整型
                if (this.frequency != _frequency)
                {
                    this.frequency = _frequency;
                    this.intervalTime = 1000 / this.frequency;
                    this.xCount = this.showTimes * this.frequency;
                }
            }

            for (int i = 0; i < channelCount; i++)
            {
                ColumnData columnData = new ColumnData();
                dataList.Add(columnData);
            }
            uiDataGridView1.DataSource = dataList;

            uiDataGridView1.ReadOnly = true;

            R = new Random();

            //定时器刷新
            this.timerChart = new System.Windows.Forms.Timer()
            {
                Interval = (int)(this.intervalTime)
            };
            this.timerChart.Tick += TimerChart_Tick;
            this.timerChart.Start();

            var sql = @"select * from sensor_table";
            var dt = MainForm.db.SelectDataTable(sql);

            for (int i = 0; i < dt.Rows.Count; i++)
            {
                var row = dt.Rows[i];
                SensorEntity sensorEntity = new SensorEntity()
                {
                    ID = (int)row["ID"],
                    SensorName = row["SensorName"] != DBNull.Value ? row["SensorName"].ToString() : "",
                    SensorType = row["SensorType"] != DBNull.Value ? (int)row["SensorType"] : 0,
                    ChannelIndex = row["ChannelIndex"] != DBNull.Value ? (int)row["ChannelIndex"] : 0,
                    SensorIndex = row["SensorIndex"] != DBNull.Value ? (int)row["SensorIndex"] : 0,
                    WaveCount = row["WaveCount"] != DBNull.Value ? (int)row["WaveCount"] : 0,
                    Expression = row["Expression"] != DBNull.Value ? row["Expression"].ToString() : "",
                    Description = row["Description"] != DBNull.Value ? row["Description"].ToString() : "",
                    Param1 = row["Param1"] != DBNull.Value ? row["Param1"].ToString() : "",
                    Param2 = row["Param2"] != DBNull.Value ? row["Param2"].ToString() : "",
                    Param3 = row["Param3"] != DBNull.Value ? row["Param3"].ToString() : "",
                    Param4 = row["Param4"] != DBNull.Value ? row["Param4"].ToString() : "",
                    Param5 = row["Param5"] != DBNull.Value ? row["Param5"].ToString() : "",
                    Param6 = row["Param6"] != DBNull.Value ? row["Param6"].ToString() : "",
                    Param7 = row["Param7"] != DBNull.Value ? row["Param7"].ToString() : "",
                    Param8 = row["Param8"] != DBNull.Value ? row["Param8"].ToString() : "",
                    Param9 = row["Param9"] != DBNull.Value ? row["Param9"].ToString() : "",
                    Param10 = row["Param10"] != DBNull.Value ? row["Param10"].ToString() : "",
                    Param11 = row["Param11"] != DBNull.Value ? row["Param11"].ToString() : "",
                    Param12 = row["Param12"] != DBNull.Value ? row["Param12"].ToString() : "",
                    Param13 = row["Param13"] != DBNull.Value ? row["Param13"].ToString() : "",
                    Param14 = row["Param14"] != DBNull.Value ? row["Param14"].ToString() : "",
                    Param15 = row["Param15"] != DBNull.Value ? row["Param15"].ToString() : "",
                    Param16 = row["Param16"] != DBNull.Value ? row["Param16"].ToString() : ""
                };
                if (entityList.ContainsKey((int)row["ChannelIndex"]))
                {
                    Dictionary<int, SensorEntity> entityMap = new Dictionary<int, SensorEntity>();
                    entityList.TryGetValue((int)row["ChannelIndex"], out entityMap);
                    entityMap.Add((int)row["SensorIndex"], sensorEntity);
                    entityList.AddOrUpdate((int)row["ChannelIndex"], entityMap);
                }
                else
                {
                    Dictionary<int, SensorEntity> entityMap = new Dictionary<int, SensorEntity>();
                    entityMap.Add((int)row["SensorIndex"], sensorEntity);
                    entityList.Add((int)row["ChannelIndex"], entityMap);
                }
            }
        }

        private void InitIntfaceData()
        {
            //得到主机名
            string HostName = Dns.GetHostName();
            //通过主机名获取IP
            IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
            //可变数组
            ArrayList addressList = new ArrayList();

            addressList.Add("0.0.0.0");
            addressList.Add("127.0.0.1");

            for (int i = 0; i < IpEntry.AddressList.Length; i++)
            {
                //从IP地址列表中筛选出IPv4类型的IP地址
                //AddressFamily.InterNetwork表示此IP为IPv4,
                //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                {
                    addressList.Add(IpEntry.AddressList[i].ToString());
                }
            }
            int selectedIndex = 0;
            string myAddress = ConfigurationManager.AppSettings["cbMyAddress"];
            //固定大小
            addressList.TrimToSize();
            object[] adList = new object[addressList.Count];
            for (int i = 0; i < addressList.Count; i++)
            {
                adList[i] = addressList[i];
                if (myAddress.Equals(adList[i]))
                {
                    selectedIndex = i;
                }
            }
            this.cbMyAddress.Items.AddRange(adList);
            this.cbMyAddress.SelectedIndex = selectedIndex;
            this.tbMyPort.Text = ConfigurationManager.AppSettings["tbMyPort"];
        }

        //窗口关闭事件委托，在窗口关闭事件前处理
        private void Page1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (receiveThread != null)
            {
                receiveThread.Interrupt();
            }
            if (serverSocket != null)
            {
                if (serverSocket.Connected)
                {
                    serverSocket.Disconnect(false);
                }
                serverSocket.Close();
                serverSocket.Dispose();
            }
            //if (this.isSaveWaveLength)
            //{
            //    if (this.excelApp != null)
            //    {
            //        this.excelApp.DisplayAlerts = false;
            //        if (this.workbook != null)
            //        {
            //            this.workbook.Close(false);
            //            this.workbook = null;
            //        }
            //        this.excelApp.Quit();
            //        this.excelApp = null;
            //    }
            //}
            //fs.Close();
            //fs.Dispose();
            //sw.Close();
            //sw.Dispose();
            //sw = null;
        }

        private void TimerChart_Tick(object sender, EventArgs e)
        {

        }

        private void ChartLineRefresh(int channelIndex, ChartValues<DateTimePoint> chartValues)
        {
            try
            {
                if (OscChart.Series.Count < chartValues.Count)
                {
                    InitChartConfig(channelIndex, chartValues.Count);
                }
                // 假设你在非UI线程上
                if (OscChart.InvokeRequired)
                {
                    OscChart.Invoke(new System.Action(() =>
                    {
                        for (int index = 0; index < chartValues.Count; index++)
                        {
                            //添加y值，chartValues为波长1数据
                            OscChart.Series[index].Values.Add(chartValues[index]);
                            int _count = OscChart.Series[index].Values.Count;
                            if (_count > this.xCount)
                            {
                                //只关心前面30个数据
                                OscChart.Series[index].Values.RemoveAt(0);
                            }
                        }
                        // 直接进行操作，因为当前线程就是UI线程
                        //OscChart.AxisX[0].MaxValue = this.xTimes;
                        //OscChart.AxisX[0].MaxValue = DateTime.Now.AddSeconds(60).Ticks;
                        //最小值，不写则会根据数据自动变化。
                        //OscChart.AxisX[0].MinValue = this.xTimes - this.xCount;
                        //OscChart.AxisX[0].MinValue = DateTime.Now.AddSeconds(-60).Ticks;
                        OscChart.Refresh();
                    }));
                }
                else
                {
                    for (int index = 0; index < chartValues.Count; index++)
                    {
                        //添加y值，chartValues为波长1数据
                        OscChart.Series[index].Values.Add(chartValues[index]);
                        int _count = OscChart.Series[index].Values.Count;
                        if (_count > this.xCount)
                        {
                            //只关心前面30个数据
                            OscChart.Series[index].Values.RemoveAt(0);
                        }
                    }
                    // 直接进行操作，因为当前线程就是UI线程
                    //OscChart.AxisX[0].MaxValue = this.xTimes;
                    //OscChart.AxisX[0].MaxValue = DateTime.Now.AddSeconds(60).Ticks;
                    //最小值，不写则会根据数据自动变化。
                    //OscChart.AxisX[0].MinValue = this.xTimes - this.xCount;
                    //OscChart.AxisX[0].MinValue = DateTime.Now.AddSeconds(-60).Ticks;              
                    OscChart.Refresh();

                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {
            }
        }

        private void InitChartConfig()
        {
            string title = "值";
            try
            {
                if (OscChart.Series.Count > 0)
                {
                    // 假设你在非UI线程上
                    if (OscChart.InvokeRequired)
                    {
                        OscChart.Invoke(new System.Action(() =>
                        {

                            OscChart.Series.Clear();
                            OscChart.AxisX.Clear();
                            OscChart.AxisY.Clear();
                        }));
                    }
                    else
                    {
                        OscChart.Series.Clear();
                        OscChart.AxisX.Clear();
                        OscChart.AxisY.Clear();
                    }
                }
                if (OscChart.InvokeRequired)
                {
                    OscChart.Invoke(new System.Action(() =>
                    {
                        OscChart.Series.Add(new LiveCharts.Wpf.StackedAreaSeries
                        {
                            //绑定C#代码中的实际数据值。
                            Values = InitChartValuesEmptyData(),
                            //坐标轴名称。
                            Title = title,
                            //曲线下方填充颜色。Transparent透明，即不填充。
                            Fill = System.Windows.Media.Brushes.Transparent,
                            //曲线颜色。
                            //Stroke = System.Windows.Media.Brushes.Transparent,
                            //数据点的外观。{x:Null}即不标出，只显示曲线。{x:Static lvc:DefaultGeometries.Circle} 则将数据点用圆形标出。形状有 Circle、Cross、Diamond、Square、Triangle可选。
                            PointGeometry = DefaultGeometries.Circle,
                            //数据点形状的大小。
                            PointGeometrySize = 15,
                            //曲线是否圆滑。如为 1则圆滑曲线，为 0则不圆滑，为折线。
                            LineSmoothness = 1,
                            //曲线粗细程度，越大越粗。
                            StrokeThickness = 4,
                            //设置点的颜色
                            PointForeground = System.Windows.Media.Brushes.Blue,
                            //曲线对应Y坐标轴，序号从 0开始，默认 0。
                            ScalesYAt = 0
                        });
                        OscChart.AxisX.Add(new LiveCharts.Wpf.Axis
                        {
                            //Title = "时间",
                            //设置X轴的最小值为当前时间减去60秒的Ticks，最大值为当前时间加上60秒的Ticks
                            //最小值，不写则会根据数据自动变化。
                            //MinValue = this.xTimes - this.xCount
                            //MinValue = DateTime.Now.AddSeconds(-60).Ticks,
                            //最大值，不写则会根据数据自动变化。
                            //MaxValue = this.xTimes,
                            //MaxValue = DateTime.Now.AddSeconds(60).Ticks,
                            //设置单位宽度为1秒
                            MinWidth = TimeSpan.FromSeconds(1).Ticks,
                            //设置最小步长为1秒，确保每秒都有刻度
                            MinRange = TimeSpan.FromSeconds(1).Ticks,
                            //字体大小
                            FontSize = 12,
                            DisableAnimations = true,
                            Foreground = System.Windows.Media.Brushes.Black,
                            // 标签格式，如把数值格式化为时间，设置标签格式化器，以便每个刻度都显示为时间
                            //LabelFormatter = value => new DateTime((long)value).ToString("HH:mm:ss.ff"),
                            //LabelFormatter = value => value.ToString(),
                            //LabelFormatter = value => new System.DateTime(System.DateTime.Now.Ticks - (long)(this.xNumber - (long)value) * this.intervalTime * 10000).ToString("mm:ss"),
                            LabelFormatter = value => (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)value)).ToString("HH:mm:ss.fff"),
                            //网格线，IsEnabled决定是否显示，Step为网格一格的大小，不写则自动。
                            //Separator = new Separator
                            //{
                            //    Step = 1,
                            //    IsEnabled = true
                            //}
                        });
                        OscChart.AxisY.Add(new LiveCharts.Wpf.Axis
                        {
                            //坐标轴名称。
                            Title = title,
                            //字体大小
                            FontSize = 12,
                            //名称和刻度的颜色。
                            Foreground = System.Windows.Media.Brushes.DodgerBlue,
                            //坐标轴位置。
                            Position = AxisPosition.LeftBottom
                        });
                    }));
                }
                else
                {
                    OscChart.Series.Add(new LiveCharts.Wpf.StackedAreaSeries
                    {
                        //绑定C#代码中的实际数据值。
                        Values = InitChartValuesEmptyData(),
                        //坐标轴名称。
                        Title = title,
                        //曲线下方填充颜色。Transparent透明，即不填充。
                        Fill = System.Windows.Media.Brushes.Transparent,
                        //曲线颜色。
                        //Stroke = System.Windows.Media.Brushes.Transparent,
                        //数据点的外观。{x:Null}即不标出，只显示曲线。{x:Static lvc:DefaultGeometries.Circle} 则将数据点用圆形标出。形状有 Circle、Cross、Diamond、Square、Triangle可选。
                        PointGeometry = DefaultGeometries.Circle,
                        //数据点形状的大小。
                        PointGeometrySize = 15,
                        //曲线是否圆滑。如为 1则圆滑曲线，为 0则不圆滑，为折线。
                        LineSmoothness = 1,
                        //曲线粗细程度，越大越粗。
                        StrokeThickness = 4,
                        //设置点的颜色
                        PointForeground = System.Windows.Media.Brushes.Blue,
                        //曲线对应Y坐标轴，序号从 0开始，默认 0。
                        ScalesYAt = 0
                    });
                    OscChart.AxisX.Add(new LiveCharts.Wpf.Axis
                    {
                        //Title = "时间",
                        //设置X轴的最小值为当前时间减去60秒的Ticks，最大值为当前时间加上60秒的Ticks
                        //最小值，不写则会根据数据自动变化。
                        //MinValue = this.xTimes - this.xCount
                        //MinValue = DateTime.Now.AddSeconds(-60).Ticks,
                        //最大值，不写则会根据数据自动变化。
                        //MaxValue = this.xTimes,
                        //MaxValue = DateTime.Now.AddSeconds(60).Ticks,
                        //设置单位宽度为1秒
                        MinWidth = TimeSpan.FromSeconds(1).Ticks,
                        //设置最小步长为1秒，确保每秒都有刻度
                        MinRange = TimeSpan.FromSeconds(1).Ticks,
                        //字体大小
                        FontSize = 12,
                        DisableAnimations = true,
                        Foreground = System.Windows.Media.Brushes.Black,
                        // 标签格式，如把数值格式化为时间，设置标签格式化器，以便每个刻度都显示为时间
                        //LabelFormatter = value => new DateTime((long)value).ToString("HH:mm:ss.ff"),
                        //LabelFormatter = value => value.ToString(),
                        //LabelFormatter = value => new System.DateTime(System.DateTime.Now.Ticks - (long)(this.xNumber - (long)value) * this.intervalTime * 10000).ToString("mm:ss"),
                        LabelFormatter = value => (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)value)).ToString("HH:mm:ss.fff"),
                        //网格线，IsEnabled决定是否显示，Step为网格一格的大小，不写则自动。
                        //Separator = new Separator
                        //{
                        //    Step = 1,
                        //    IsEnabled = true
                        //}
                    });
                    OscChart.AxisY.Add(new LiveCharts.Wpf.Axis
                    {
                        //坐标轴名称。
                        Title = title,
                        //字体大小
                        FontSize = 12,
                        //名称和刻度的颜色。
                        Foreground = System.Windows.Media.Brushes.DodgerBlue,
                        //坐标轴位置。
                        Position = AxisPosition.LeftBottom
                    });
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {
            }
        }

        private void InitChartConfig(int channelID, int yIndex)
        {
            string title = "值";
            try
            {
                if (OscChart.Series.Count > 0)
                {
                    // 假设你在非UI线程上
                    if (OscChart.InvokeRequired)
                    {
                        OscChart.Invoke(new System.Action(() =>
                        {
                            OscChart.Series.Clear();
                            OscChart.AxisX.Clear();
                            OscChart.AxisY.Clear();
                        }));
                    }
                    else
                    {
                        OscChart.Series.Clear();
                        OscChart.AxisX.Clear();
                        OscChart.AxisY.Clear();
                    }
                }
                if (OscChart.InvokeRequired)
                {
                    OscChart.Invoke(new System.Action(() =>
                    {
                        for (int y = 1; y <= yIndex; y++)
                        {
                            string title1 = "通道-" + channelID + "-传感器-" + y;
                            OscChart.Series.Add(new LiveCharts.Wpf.StackedAreaSeries
                            {
                                //绑定C#代码中的实际数据值。
                                Values = InitChartValuesEmptyData(),
                                //坐标轴名称。
                                Title = title1,
                                //曲线下方填充颜色。Transparent透明，即不填充。
                                Fill = System.Windows.Media.Brushes.Transparent,
                                //曲线颜色。
                                //Stroke = System.Windows.Media.Brushes.Transparent,
                                //数据点的外观。{x:Null}即不标出，只显示曲线。{x:Static lvc:DefaultGeometries.Circle} 则将数据点用圆形标出。形状有 Circle、Cross、Diamond、Square、Triangle可选。
                                PointGeometry = DefaultGeometries.Circle,
                                //数据点形状的大小。
                                PointGeometrySize = 15,
                                //曲线是否圆滑。如为 1则圆滑曲线，为 0则不圆滑，为折线。
                                LineSmoothness = 1,
                                //曲线粗细程度，越大越粗。
                                StrokeThickness = 4,
                                //设置点的颜色
                                PointForeground = System.Windows.Media.Brushes.Blue,
                                //曲线对应Y坐标轴，序号从 0开始，默认 0。
                                ScalesYAt = 0
                            });
                        }
                        OscChart.AxisX.Add(new LiveCharts.Wpf.Axis
                        {
                            //Title = "时间",
                            //设置X轴的最小值为当前时间减去60秒的Ticks，最大值为当前时间加上60秒的Ticks
                            //最小值，不写则会根据数据自动变化。
                            //MinValue = this.xTimes - this.xCount
                            //MinValue = DateTime.Now.AddSeconds(-60).Ticks,
                            //最大值，不写则会根据数据自动变化。
                            //MaxValue = this.xTimes,
                            //MaxValue = DateTime.Now.AddSeconds(60).Ticks,
                            //设置单位宽度为1秒
                            MinWidth = TimeSpan.FromSeconds(1).Ticks,
                            //设置最小步长为1秒，确保每秒都有刻度
                            MinRange = TimeSpan.FromSeconds(1).Ticks,
                            //字体大小
                            FontSize = 12,
                            DisableAnimations = true,
                            Foreground = System.Windows.Media.Brushes.Black,
                            // 标签格式，如把数值格式化为时间，设置标签格式化器，以便每个刻度都显示为时间
                            //LabelFormatter = value => new DateTime((long)value).ToString("HH:mm:ss.ff"),
                            //LabelFormatter = value => value.ToString(),
                            //LabelFormatter = value => new System.DateTime(System.DateTime.Now.Ticks - (long)(this.xNumber - (long)value) * this.intervalTime * 10000).ToString("mm:ss"),
                            LabelFormatter = value => (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)value)).ToString("HH:mm:ss.fff"),
                            //网格线，IsEnabled决定是否显示，Step为网格一格的大小，不写则自动。
                            //Separator = new Separator
                            //{
                            //    Step = 1,
                            //    IsEnabled = true
                            //}
                        });
                        OscChart.AxisY.Add(new LiveCharts.Wpf.Axis
                        {
                            //坐标轴名称。
                            Title = title,
                            //字体大小
                            FontSize = 12,
                            //名称和刻度的颜色。
                            Foreground = System.Windows.Media.Brushes.DodgerBlue,
                            //坐标轴位置。
                            Position = AxisPosition.LeftBottom
                        });
                    }));
                }
                else
                {
                    for (int y = 0; y < yIndex; y++)
                    {
                        string title1 = "通道-" + channelID + "-传感器-" + y;
                        OscChart.Series.Add(new LiveCharts.Wpf.StackedAreaSeries
                        {
                            //绑定C#代码中的实际数据值。
                            Values = InitChartValuesEmptyData(),
                            //坐标轴名称。
                            Title = title1,
                            //曲线下方填充颜色。Transparent透明，即不填充。
                            Fill = System.Windows.Media.Brushes.Transparent,
                            //曲线颜色。
                            //Stroke = System.Windows.Media.Brushes.Transparent,
                            //数据点的外观。{x:Null}即不标出，只显示曲线。{x:Static lvc:DefaultGeometries.Circle} 则将数据点用圆形标出。形状有 Circle、Cross、Diamond、Square、Triangle可选。
                            PointGeometry = DefaultGeometries.Circle,
                            //数据点形状的大小。
                            PointGeometrySize = 15,
                            //曲线是否圆滑。如为 1则圆滑曲线，为 0则不圆滑，为折线。
                            LineSmoothness = 1,
                            //曲线粗细程度，越大越粗。
                            StrokeThickness = 4,
                            //设置点的颜色
                            PointForeground = System.Windows.Media.Brushes.Blue,
                            //曲线对应Y坐标轴，序号从 0开始，默认 0。
                            ScalesYAt = 0
                        });
                    }
                    OscChart.AxisX.Add(new LiveCharts.Wpf.Axis
                    {
                        //Title = "时间",
                        //设置X轴的最小值为当前时间减去60秒的Ticks，最大值为当前时间加上60秒的Ticks
                        //最小值，不写则会根据数据自动变化。
                        //MinValue = this.xTimes - this.xCount
                        //MinValue = DateTime.Now.AddSeconds(-60).Ticks,
                        //最大值，不写则会根据数据自动变化。
                        //MaxValue = this.xTimes,
                        //MaxValue = DateTime.Now.AddSeconds(60).Ticks,
                        //设置单位宽度为1秒
                        MinWidth = TimeSpan.FromSeconds(1).Ticks,
                        //设置最小步长为1秒，确保每秒都有刻度
                        MinRange = TimeSpan.FromSeconds(1).Ticks,
                        //字体大小
                        FontSize = 12,
                        DisableAnimations = true,
                        Foreground = System.Windows.Media.Brushes.Black,
                        // 标签格式，如把数值格式化为时间，设置标签格式化器，以便每个刻度都显示为时间
                        //LabelFormatter = value => new DateTime((long)value).ToString("HH:mm:ss.ff"),
                        //LabelFormatter = value => value.ToString(),
                        //LabelFormatter = value => new System.DateTime(System.DateTime.Now.Ticks - (long)(this.xNumber - (long)value) * this.intervalTime * 10000).ToString("mm:ss"),
                        LabelFormatter = value => (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)value)).ToString("HH:mm:ss.fff"),
                        //网格线，IsEnabled决定是否显示，Step为网格一格的大小，不写则自动。
                        //Separator = new Separator
                        //{
                        //    Step = 1,
                        //    IsEnabled = true
                        //}
                    });
                    OscChart.AxisY.Add(new LiveCharts.Wpf.Axis
                    {
                        //坐标轴名称。
                        Title = title,
                        //字体大小
                        FontSize = 12,
                        //名称和刻度的颜色。
                        Foreground = System.Windows.Media.Brushes.DodgerBlue,
                        //坐标轴位置。
                        Position = AxisPosition.LeftBottom
                    });
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {
            }
        }

        private static ChartValues<DateTimePoint> InitChartValuesEmptyData()
        {
            ChartValues<DateTimePoint> chartValues = new ChartValues<DateTimePoint>();
            return chartValues;
        }


        // 接收发送给本机IP对应端口号的数据报

        public static List<int> indexOfByteArray(byte[] data, byte[] pattern)
        {
            List<int> matchedPos = new List<int>();

            if (data.Length == 0 || data.Length < pattern.Length) return matchedPos;

            int end = data.Length - pattern.Length;
            bool matched = false;

            for (int i = 0; i <= end; i++)
            {
                for (int j = 0; j < pattern.Length || !(matched = (j == pattern.Length)); j++)
                {
                    if (data[i + j] != pattern[j]) break;
                }
                if (matched)
                {
                    matched = false;
                    matchedPos.Add(i);
                }
            }
            return matchedPos;
        }

        private void btnOpenServer_Click(object sender, EventArgs e)
        {
            //停用按钮，防止多次点击
            this.btnOpenServer.Enabled = false;            
            //启动TCP服务
            if (isOpenServerPort == false)
            {
                try
                {
                    int myPort = getPort(tbMyPort.Text);
                    if (myPort == -1)
                    {
                        MessageBox.Show("服务器端口格式不对！（值：0 - 65535）", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        this.btnOpenServer.Enabled = true;
                        return;
                    }
                    if (cbMyAddress.SelectedIndex == 0)
                    {
                        tcpServer = new TcpListener(IPAddress.Any, myPort);
                    }
                    else
                    {
                        tcpServer = new TcpListener(IPAddress.Parse(cbMyAddress.SelectedItem.ToString()), myPort);
                    }
                    tcpServer.Start();
                    tcpServer.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), tcpServer);
                    //启动监听
                    isOpenServerPort = true;
                    //设置界面为服务打开状态
                    lbServerState.Text = "服务器已开启";
                    lbServerState.ForeColor = System.Drawing.Color.Red;
                    btnOpenServer.Text = "关闭服务端口";
                    //UI显示 设置控件不可用
                    cbMyAddress.Enabled = false;
                    tbMyPort.Enabled = false;
                    RecordUserOperation("打开服务端口");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    log.Error(ex.Message);
                }
                finally
                {
                    //按钮启用
                    btnOpenServer.Enabled = true;
                }
            }
            else
            {
                //关闭监听
                tcpServer.Stop();
                try
                {
                    //循环关闭客户端列表监听
                    foreach (Thread itemthread in tcpClientThreads)
                    {
                        itemthread.Abort();
                    }
                    if (receiveThread != null)
                    {
                        receiveThread.Abort();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("接收发送给本机IP对应端口号的数据报错！" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    log.Error(ex.Message);
                }
                finally
                {

                }
                isOpenServerPort = false;
                //设置界面为服务关闭状态
                lbServerState.Text = "服务器已关闭";
                lbServerState.ForeColor = System.Drawing.Color.Blue;
                btnOpenServer.Text = "打开服务端口";
                //UI显示 设置控件可用
                cbMyAddress.Enabled = true;
                tbMyPort.Enabled = true;
                RecordUserOperation("关闭服务端口");
            }
            btnOpenServer.Enabled = true;
        }

        private void ReceiveMessage(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();
            byte[] message = new byte[8192];
            int bytesRead;

            try
            {
                // 读取客户端发送的数据
                while ((bytesRead = clientStream.Read(message, 0, message.Length)) != 0)
                {
                    // 将接收到的数据转换成字符串并显示
                    string clientMessage = Encoding.ASCII.GetString(message, 0, bytesRead);
                    log.Info("Received: " + clientMessage);
                    ReceiveWaveDataCFBG(message);

                    string backMessage = "01";
                    // 将接收到的数据回发给客户端（可选）
                    byte[] buffer = Encoding.ASCII.GetBytes(backMessage);
                    clientStream.Write(buffer, 0, buffer.Length);
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {
                // 关闭流和客户端连接
                clientStream.Close();
                tcpClient.Close();
            }
        }

        private void AcceptCallback(IAsyncResult asyncResult)
        {
            TcpListener tcpServerListener = asyncResult.AsyncState as TcpListener;
            try
            {
                //初始化连接的客户端
                TcpClient tcpClient = tcpServerListener.EndAcceptTcpClient(asyncResult);

                tcpClientList.Add(tcpClient);

                //tcpServerListener.BeginAcceptTcpClient(new AsyncCallback(AcceptCallback), tcpServerListener);//继续监听客户端连接

                Thread clientThread = new Thread(new ParameterizedThreadStart(ReceiveMessage));
                clientThread.IsBackground = false;
                clientThread.Start(tcpClient);
                tcpClientThreads.Add(clientThread);
            }
            catch (ObjectDisposedException ex)
            { //监听被关闭
                log.Error(ex.Message);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
        //CFBG光栅解调仪设备通信协议解析
        private void ReceiveWaveDataCFBG(byte[] revData)
        {
            long nowTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            long interval = (nowTime - this.lastTime);
            if (interval < this.intervalTime)
            {
                return;
            }
            else
            {
                this.lastTime = nowTime;
            }
            try
            {
                //系统标识 string 4Byte CFBG
                byte[] sysSignArray = new byte[4];
                Array.Copy(revData, 0, sysSignArray, 0, 4);
                string sysSign = new ASCIIEncoding().GetString(sysSignArray);
                if ("CFBG".EqualsAnyIgnoreCase(sysSign))
                {
                    //数据总长 第2部分数据总字节数，帧数据长度+2字节CRC16验证 int 4Byte 
                    byte[] lengthArray = new byte[8];
                    Array.Copy(revData, 4, lengthArray, 0, 8);
                    Array.Reverse(lengthArray);
                    long length = BitConverter.ToInt64(lengthArray);
                    lengthArray = null;
                    if (length == 0)
                    {
                        log.Info("length:" + length);
                    }
                    //帧数	第2部分帧数, 等于设备采集频率 int 4Byte
                    byte[] frequenceArray = new byte[4];
                    Array.Copy(revData, 8, frequenceArray, 0, 4);
                    Array.Reverse(frequenceArray);
                    int frequence = BitConverter.ToInt32(frequenceArray);
                    frequenceArray = null;
                    //每帧数据字符串首位相接，可通过标识头“CFBG”或第1部分的帧数进行切分；整部分最后2字节为CRC16验证码，如验证成功服务端需向客户端发送2字节“01”否则发送“02”，之后客户端会重发该条数据。
                    byte[] tmpArray = new byte[revData.Length - 12];
                    Array.Copy(revData, 12, tmpArray, 0, revData.Length - 12);
                    List<int> matchedPos = indexOfByteArray(tmpArray, sysSignArray);
                    if (matchedPos.Count <= 0) { return; }
                    for (int index = 0; index < matchedPos.Count - 1; index++)
                    {
                        int begPos = matchedPos[index];
                        int endPos = matchedPos[index + 1];
                        if (endPos - begPos <= 12)
                        {
                            continue;
                        }
                        byte[] tmpDataArray = new byte[endPos - begPos];
                        Array.Copy(tmpArray, begPos, tmpDataArray, 0, endPos - begPos);

                        //系统标识 string 4Byte CFBG
                        sysSignArray = new byte[4];
                        Array.Copy(tmpDataArray, 0, sysSignArray, 0, 4);
                        sysSign = new ASCIIEncoding().GetString(sysSignArray);
                        //2Byte 16 bit的unsigned short： bit 0~15：年
                        byte[] yearArray = new byte[2];
                        Array.Copy(tmpDataArray, 4, yearArray, 0, 2);
                        Array.Reverse(yearArray);
                        ushort year = BitConverter.ToUInt16(yearArray);
                        yearArray = null;
                        //2Byte 16 bit的unsigned short： bit 0~7：日 bit 8~15：月
                        char[] monthArray = new char[1];
                        Array.Copy(tmpDataArray, 6, monthArray, 0, 1);
                        ushort month = monthArray[0];
                        monthArray = null;

                        char[] dayArray = new char[1];
                        Array.Copy(tmpDataArray, 7, dayArray, 0, 1);
                        ushort day = dayArray[0];
                        dayArray = null;
                        //2Byte 16 bit的unsigned short：bit 0~7：分 bit 8~15：时
                        char[] hourArray = new char[1];
                        Array.Copy(tmpDataArray, 8, hourArray, 0, 1);
                        ushort hour = hourArray[0];
                        hourArray = null;

                        char[] minArray = new char[1];
                        Array.Copy(tmpDataArray, 9, minArray, 0, 1);
                        ushort min = minArray[0];
                        minArray = null;
                        //4Byte 32 bit的float：秒.毫秒
                        byte[] secArray = new byte[4];
                        Array.Copy(tmpDataArray, 10, secArray, 0, 4);
                        Array.Reverse(secArray);
                        float sec = BitConverter.ToSingle(secArray);
                        secArray = null;

                        double millSec = (sec - Math.Floor(sec))*1000;

                        DateTime dateTime = new DateTime(year, month, day, hour, min, (int)sec, (int)millSec, DateTimeKind.Utc);
                        long timestamp = new DateTimeOffset(dateTime).ToUnixTimeMilliseconds();

                        //2Byte unsigned short 接下来的数据长度，单位为字节
                        byte[] lengthArray1 = new byte[2];
                        Array.Copy(tmpDataArray, 14, lengthArray1, 0, 2);
                        Array.Reverse(lengthArray1);
                        ushort length1 = BitConverter.ToUInt16(lengthArray1);
                        lengthArray1 = null;
                        //2Byte unsigned short 全局告警 每bit代表一种告警，bit位为1告警，为0正常。 bit 0：设备与软件连接中断 bit 1：传感器故障 bit 2：设备温度告警
                        //告警可以累加，所有的告警状态不保持，即故障消除，告警消失。当没有启动采集时，该状态bit位0，当启动后，网络通信中断，该状态为1（设备与软件连接中断）
                        byte[] alarmArray = new byte[2];
                        Array.Copy(tmpDataArray, 16, alarmArray, 0, 2);
                        Array.Reverse(alarmArray);
                        ushort alarm = BitConverter.ToUInt16(alarmArray);
                        //1Byte uchar 采集状态 1：正常采集 0：停止采集
                        char[] collectArray = new char[1];
                        Array.Copy(tmpDataArray, 18, collectArray, 0, 1);
                        char collect = collectArray[0];
                        if (collect == 0)
                        {
                            break;
                        }
                        collectArray = null;

                        //1Byte uchar 设备通道数 通道范围：1~N
                        char[] channelArray = new char[1];
                        Array.Copy(tmpDataArray, 19, channelArray, 0, 1);
                        char channelCut = channelArray[0];
                        channelArray = null;


                        Dictionary<int, int> channelSensorCntMap = new Dictionary<int, int>();
                        int beginIndex = 20;
                        for (int channelId = 0; channelId < channelCut; channelId++)
                        {
                            //1Byte uchar 通道0 传感器数量 bit 0~7：通道0传感器数量，此为虚拟通道，组合光栅类型为自定义
                            char[] channelSensorCntArray = new char[1];
                            Array.Copy(tmpDataArray, beginIndex + channelId, channelSensorCntArray, 0, 1);
                            char channelSensorCnt = channelSensorCntArray[0];
                            channelSensorCntMap.Add(channelId, channelSensorCnt);
                            channelSensorCntArray = null;
                        }
                        beginIndex = beginIndex + channelCut;
                        List<SensorDeviceData> sensorDeviceDataList = new List<SensorDeviceData>();
                        for (int channelId = 0; channelId < channelCut; channelId++)
                        {
                            int channelSensorCnt = 0;
                            channelSensorCntMap.TryGetValue(channelId, out channelSensorCnt);
                            string[] waveValues = new string[rasterCount + 2];
                            waveValues[0] = channelId + "";
                            waveValues[1] = channelSensorCnt + "";
                            for (int sensorIndex = 0; sensorIndex < channelSensorCnt; sensorIndex++)
                            {
                                //2Byte ushort 传感器n编号(n从1开始)传感器编号由两部分组成：通道号和传感器在通道中的序号。
                                //bit 8~15：通道号，范围01 - 08 bit 0~7：传感器在通道中的序号，范围01 - 30 例如020F，表示2通道的15号传感器。
                                byte[] sensorIdArray1 = new byte[2];
                                Array.Copy(tmpDataArray, beginIndex, sensorIdArray1, 0, 2);
                                string sensorId = BitConverter.ToString(sensorIdArray1);
                                sensorIdArray1 = null;
                                //4Byte int 传感器n原始波长 传感器的传输值为实际值乘以1000，客户端必须把读取的值除以 1000 才是传感器的实际值，比如传感器的实际值为 1556.34521,则传输的值为 155634521
                                byte[] sensorWave0Array = new byte[4];
                                Array.Copy(tmpDataArray, beginIndex + 2, sensorWave0Array, 0, 4);
                                Array.Reverse(sensorWave0Array);
                                uint sensorWave1 = BitConverter.ToUInt32(sensorWave0Array);
                                double sensorWave10 = (double)DivideAndRound(sensorWave1, 1000, 3);
                                sensorWave0Array = null;
                                //4Byte int 传感器n校准波长 传感器的传输值为实际值乘以1000，客户端必须把读取的值除以 1000 才是传感器的实际值，比如传感器的实际值为 1556.34512, 则传输的值为 155634512
                                byte[] sensorWave1Array = new byte[4];
                                Array.Copy(tmpDataArray, beginIndex + 6, sensorWave1Array, 0, 4);
                                Array.Reverse(sensorWave1Array);
                                uint sensorWave2 = BitConverter.ToUInt32(sensorWave1Array);
                                double sensorWave20 = (double)DivideAndRound(sensorWave2, 1000, 3);
                                sensorWave1Array = null;
                                //2Byte ushort 传感器n类型  0：未定义1：自定义2：温度 3：应变4：压力5：流量 6：位移7：加速度
                                byte[] sensorTypeArray = new byte[2];
                                Array.Copy(tmpDataArray, beginIndex + 10, sensorTypeArray, 0, 2);
                                Array.Reverse(sensorTypeArray);
                                ushort sensorType = BitConverter.ToUInt16(sensorTypeArray);
                                sensorTypeArray = null;
                                //4Byte int 传感器n物理量 传感器的传输值为实际值乘以1000，客户端必须把读取的值除以 1000 才是传感器的实际值，比如传感器的实际值为 12.34512，则传输的值为 1234512
                                byte[] sensorPhyArray = new byte[4];
                                Array.Copy(tmpDataArray, beginIndex + 12, sensorPhyArray, 0, 4);
                                Array.Reverse(sensorPhyArray);
                                uint sensorPhy = BitConverter.ToUInt32(sensorPhyArray);
                                double sensorPhy1 = (double)DivideAndRound(sensorPhy, 1000, 3);
                                sensorPhyArray = null;
                                waveValues[sensorIndex + 2] = sensorWave10 + "";
                                beginIndex = beginIndex + 16;
                                if (channelId != 0)
                                {
                                    SensorDeviceData sensorDeviceData = new SensorDeviceData();
                                    sensorDeviceData.uid = Guid.NewGuid().ToString();
                                    sensorDeviceData.device_id = "1#" + channelId + "_" + sensorIndex; 
                                    sensorDeviceData.collect_datetime = dateTime.ToString("yyyy-MM-dd HH:mm:ss.ff");
                                    sensorDeviceData.collect_time = timestamp;
                                    sensorDeviceData.channel_id = channelId;
                                    sensorDeviceData.sensor_id = sensorId;
                                    sensorDeviceData.sensor_index = sensorIndex;
                                    sensorDeviceData.sensor_type = sensorType;
                                    sensorDeviceData.sensor_count = 1;
                                    sensorDeviceData.wave_length1 = sensorWave10;
                                    sensorDeviceData.wave_length2 = sensorWave20;
                                    sensorDeviceData.count_value1 = sensorPhy1;
                                    sensorDeviceData.sensor_name = "温度传感器";
                                    sensorDeviceDataList.Add(sensorDeviceData);
                                    if ((channelId == channelIndex) && (sensorIndex == this.rasterIndex))
                                    {
                                        ChartValues<DateTimePoint> chartValues = new ChartValues<DateTimePoint>();
                                        chartValues.Add(new DateTimePoint(timestamp, sensorPhy1));
                                        ChartLineRefresh(channelId, chartValues);
                                    }
                                    if (isAlarmNotify)
                                    {
                                        if (sensorPhy1 > axialForceExceeds)
                                        {
                                            axialAlarmRepeatTimes++;
                                            if (axialAlarmRepeatTimes > repeatTimes)
                                            {
                                                string alarmNotice = "通道：" + channelId + " 温度：" + sensorPhy1 + " 连续 " + axialAlarmRepeatTimes + " 次" + "超过警告阈值：" + axialForceExceeds + "，请注意！";
                                                if (isAlarmEmail)
                                                {
                                                    SendEmail("smtp.qq.com", 587, "24139297@qq.com", "rvsdsfqwexwrbgba", alarmEmail, "告警通知", alarmNotice);
                                                }
                                                if (isAlarmPhone)
                                                {
                                                    int roundedValue = Convert.ToInt32(sensorPhy1);
                                                    string[] templateParamSet = [channelId + "", roundedValue + "", axialAlarmRepeatTimes + ""];
                                                    SendSms(alarmPhone, templateParamSet);
                                                }
                                                axialAlarmRepeatTimes = 0;
                                                RecordAlarmNotice(sensorDeviceData, alarmNotice, alarmEmail, alarmPhone);
                                            }
                                        }
                                    }
                                }
                            }
                            //显示到数据栏
                            ShowTableViewData(waveValues, channelId);
                            waveValues = null;
                        }

                        //保存传感器数据到数据库
                        if (sensorDeviceDataList.Count > 0)
                        {
                            //保存到Mysql数据库
                            if (isSaveDataToMysql)
                            {
                                SaveSensorDeviceDataToMysql(sensorDeviceDataList);
                            }
                            //保存到Txt或者Excel文件
                            if (isSaveDataToExcel)
                            {
                                SaveSensorDeviceDataToExcel(sensorDeviceDataList);
                            }
                            //保存到Txt或者Excel文件
                            if (isSaveDataToTxt)
                            {
                                SaveSensorDeviceDataToTxt(sensorDeviceDataList);
                            }
                            sensorDeviceDataList.Clear();
                            sensorDeviceDataList = null;
                        }
                        RefreshTableViewData();
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {

            }
        }

        private async Task saveRasterWaveToDBAsync(System.Data.DataTable table)
        {
            await MainForm.db.BulkCopyAsync(table, rasterTableName, timeoutSeconds, notifyAfter, callBack, cancellationToken);
        }

        private async Task saveSensorDataToDBAsync(System.Data.DataTable table)
        {
            await MainForm.db.BulkCopyAsync(table, sensorTableName, timeoutSeconds, notifyAfter, callBack, cancellationToken);
        }

        public static decimal DivideAndRound(decimal dividend, decimal divisor, int decimalPlaces)
        {
            if (divisor == 0)
            {
                throw new DivideByZeroException();
            }
            decimal result = dividend / divisor;
            return Math.Round(result, decimalPlaces);
        }

        //应变计算公式：ε＝[（W1－A1）- ((W2－A2)/ K2) * K3] /K1
        //其中：W1 为应变光栅当前测量波长，A1 为应变光栅安装完成后初始波长
        // W2 为温度光栅当前测量波长，A2 为温度光栅安装完成后初始波长
        // K1 为应变系数， K2 为温度光栅温度系数，K3 为应变光栅温度系数
        private static double CalculateStress(double W1, double W2, double A1, double A2, double K1, double K2, double K3)
        {
            double stress = (((W1 - A1) - ((W2 - A2) / K2) * K3) / K1) * 1000;

            return Math.Round(stress, 4);
        }

        //计算公式：A =[(Wa - Wa0)-(Wb-Wb0)]/k
        //其中：	Wa—a光栅测试波长(小波长)         Wa0—a光栅零点波长
        //          Wb—b光栅测试波长(大波长）        Wb0—b光栅零点波长
        //备注：初始波长不能以报告的初始波长，安装的时候要以当前环境读出来的波长为初始波长
        private static double CalculateAngle(double Wa, double Wa0, double Wb, double Wb0, double K)
        {
            double stress = ((Wa - Wa0) - (Wb - Wb0)) / K;

            return Math.Round(stress, 4);
        }

        //公式：加速度A =（W1－A1）/K
        //A为加速度，W1是测试波长，A1是原始波长，K是加速度灵敏度
        private static double CalculateAcceleration(double W1, double A1, double K)
        {
            double stress = ((W1 - A1) / K) * 1000;

            return Math.Round(stress, 4);
        }

        //位移计算公式：△L=A((λ1-λ2)-(λ10-λ20))²+B((λ1-λ2)-(λ10- λ20))+C
        // 上式中：A,B,C 为多项式系数
        // △L 为仪器长度变化量，单位 mm
        // λ1、λ2 为当前光栅的波长，单位 nm
        // λ10、λ20 为初始光栅的波长(必须用“计算参数”两光栅波长),单位 nm
        private static double CalculateDisplacement(double λ1, double λ2, double λ10, double λ20, double A, double B, double C)
        {
            double stress = A * Math.Pow(((λ1 - λ2) - (λ10 - λ20)), 2) + B * ((λ1 - λ2) - (λ10 - λ20)) + C;

            return Math.Round(stress, 4);
        }

        public void ShowTableViewData(string[] waveValues, int channelID)
        {
            ColumnData columnData = dataList[channelID];
            columnData.Time = DateTime.Now.ToString();
            columnData.ChannelNumber = waveValues[0];
            columnData.WaveNumber = waveValues[1];
            if (waveValues[2]!= null) { columnData.Sensor1 = waveValues[2]; }
            if (waveValues[3] != null) { columnData.Sensor2 = waveValues[3]; }
            if (waveValues[4] != null) { columnData.Sensor3 = waveValues[4]; }
            if (waveValues[5] != null) { columnData.Sensor4 = waveValues[5]; }
            if (waveValues[6] != null) { columnData.Sensor5 = waveValues[6]; }
            if (waveValues[7] != null) { columnData.Sensor6 = waveValues[7]; }
            if (waveValues[8] != null) { columnData.Sensor7 = waveValues[8]; }
            if (waveValues[9] != null) { columnData.Sensor8 = waveValues[9]; }
            if (waveValues[10] != null) { columnData.Sensor9 = waveValues[10]; }
            if (waveValues[11] != null) { columnData.Sensor10 = waveValues[11]; }
            if (waveValues[12] != null) { columnData.Sensor11 = waveValues[12]; }
            if (waveValues[13] != null) { columnData.Sensor12 = waveValues[13]; }
            if (waveValues[14] != null) { columnData.Sensor13 = waveValues[14]; }
            if (waveValues[15] != null) { columnData.Sensor14 = waveValues[15]; }
            if (waveValues[16] != null) { columnData.Sensor15 = waveValues[16]; }
            if (waveValues[17] != null) { columnData.Sensor16 = waveValues[17]; }
        }

        public void RefreshTableViewData()
        {
            try
            {
                if (uiDataGridView1.InvokeRequired)
                {
                    uiDataGridView1.Invoke(new System.Action(() =>
                    {
                        // 在这里安全地修改uiDataGri
                        uiDataGridView1.DataSource = dataList;
                        uiDataGridView1.Refresh();
                    }));
                }
                else
                {
                    // 直接修改，但通常不推荐，因为即使在当前线程，也应该使用Invoke来保持一致性和避免潜在问题
                    uiDataGridView1.DataSource = dataList;
                    uiDataGridView1.Refresh();
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            finally
            {
            }
        }

        public void SaveSensorDeviceDataToMysql(List<SensorDeviceData> sensorDeviceDataList)
        {
            if (isSaveDataToMysql)
            {
                foreach (SensorDeviceData sensorDeviceData in sensorDeviceDataList)
                {
                    try {
                        MainForm.db.ExecuteSql(
                            MainForm.db.Insert("sensor_device_data")
                            .SetColumn("uid", sensorDeviceData.uid)
                            .SetColumn("device_id", sensorDeviceData.device_id)
                            .SetColumn("collect_time", sensorDeviceData.collect_time)
                            .SetColumn("collect_datetime", sensorDeviceData.collect_datetime)
                            .SetColumn("channel_id", sensorDeviceData.channel_id)
                            .SetColumn("sensor_id", sensorDeviceData.sensor_id)
                            .SetColumn("sensor_name", sensorDeviceData.sensor_name)
                            .SetColumn("sensor_type", sensorDeviceData.sensor_type)
                            .SetColumn("sensor_count", sensorDeviceData.sensor_count)
                            .SetColumn("sensor_index", sensorDeviceData.sensor_index)
                            .SetColumn("wave_length1", sensorDeviceData.wave_length1)
                            .SetColumn("wave_length2", sensorDeviceData.wave_length2)
                            .SetColumn("wave_length3", sensorDeviceData.wave_length3)
                            .SetColumn("count_value1", sensorDeviceData.count_value1)
                            .SetColumn("count_value2", sensorDeviceData.count_value2)
                            .SetColumn("count_value3", sensorDeviceData.count_value3)
                            .ToSql());
                    }
                    catch(Exception ex)
                    {
                        log.Error(ex.Message);
                    }
                }
            }
        }

        public void SaveSensorDeviceDataToTxt(List<SensorDeviceData> sensorDeviceDatas)
        {
            try
            {
                if (this.sw == null)
                {
                    //CreateTxtDataFile();
                    DateTime dt = DateTime.Now;
                    string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2") + dt.Hour.ToString("D2") + dt.Minute.ToString("D2") + dt.Second.ToString("D2");
                    if (waveFilePath == "")
                    {
                        filePath = AppDomain.CurrentDomain.BaseDirectory + waveFileName + @"-" + date + @".txt";
                    }
                    else
                    {
                        filePath = waveFilePath + waveFileName + @"-" + date + @".txt";
                    }
                    sw = File.AppendText(filePath);
                    waveFileNameDate = date;
                }
                else
                {
                    DateTime dt = DateTime.Now;
                    string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2") + dt.Hour.ToString("D2") + dt.Minute.ToString("D2") + dt.Second.ToString("D2");
                    if (!waveFileNameDate.Equals(date))
                    {
                        sw.Close();
                        sw.Dispose();
                        sw = null;
                        if (waveFilePath == "")
                        {
                            filePath = AppDomain.CurrentDomain.BaseDirectory + waveFileName + @"-" + date + @".txt";
                        }
                        else
                        {
                            filePath = waveFilePath + waveFileName + @"-" + date + @".txt";
                        }
                        sw = File.AppendText(filePath);
                        waveFileNameDate = date;
                    }
                }
                if (this.sw != null)
                {
                    foreach (SensorDeviceData sensorDeviceData in sensorDeviceDatas)
                    {
                        sw.WriteLine(sensorDeviceData.ToString()); // 写入文本。
                    }
                    sw.Flush();
                }
            }
            catch(Exception ex) {
                log.Error(ex.Message);
            }

        }

        //数据保存到excel
        public void SaveSensorDeviceDataToExcel(List<SensorDeviceData> sensorDeviceDataList)
        {
            if (!this.isSaveDataToExcel)
            {
                return;
            }
            try
            {

                if (this.excelApp == null)
                {
                    CreateExcelDataFile();
                }
                if (this.workbook == null)
                {
                    MessageBox.Show("无法打开 Workbook 对象，请确认 Excel 文件是否正常");
                    return;

                }
                DateTime dt = DateTime.Now;
                int hour = dt.Hour;
                int min = dt.Minute;
                int sec = dt.Second;
                int msec = dt.Millisecond;
                string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2") + dt.Hour.ToString("D2") + dt.Minute.ToString("D2") + dt.Second.ToString("D2");
                string filePath;
                if (waveFilePath == "")
                {
                    filePath = AppDomain.CurrentDomain.BaseDirectory + waveFileName + @"-" + date + @".xlsx";
                }
                else
                {
                    filePath = waveFilePath + waveFileName + @"-" + date + @".xlsx";
                }
                if (!waveFileNameDate.Equals(date))
                {
                    this.workbook.Close(false);
                    this.workbook = null;
                    //excelApp.Quit();
                    //excelApp = null;
                    Excel.Workbooks workbooks = this.excelApp.Workbooks;
                    if (!File.Exists(filePath))
                    {
                        File.Copy(AppDomain.CurrentDomain.BaseDirectory + waveFileName + @".xlsx", filePath, true);
                    }
                    this.workbook = workbooks.Add(filePath); //加载模板
                    waveFileNameDate = date;
                }

                Excel.Sheets sheets = workbook.Sheets;
                Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1); //第一个工作薄。
                if (worksheet == null)
                {
                    MessageBox.Show("无法打开 Worksheet 对象，请确认 Excel 文件是否正常");
                    return; //工作薄中没有工作表.
                }

                int columnIndex = 1; // A列的索引是1
                int columnCount = CountColumnValues(worksheet, columnIndex);

                if ((sensorDeviceDataList != null) && (sensorDeviceDataList.Count > 0))
                {
                    //写入数值
                    for (int i = 0; i < sensorDeviceDataList.Count; i++)
                    {
                        SensorDeviceData sensorDeviceData = sensorDeviceDataList.Get(i);
                        //写入内容
                        worksheet.Cells[columnCount + 1 + i, 1] = sensorDeviceData.id;
                        worksheet.Cells[columnCount + 1 + i, 2] = sensorDeviceData.uid;
                        worksheet.Cells[columnCount + 1 + i, 3] = sensorDeviceData.device_id;
                        worksheet.Cells[columnCount + 1 + i, 4] = sensorDeviceData.collect_datetime;
                        worksheet.Cells[columnCount + 1 + i, 5] = sensorDeviceData.collect_time;
                        worksheet.Cells[columnCount + 1 + i, 6] = sensorDeviceData.channel_id;
                        worksheet.Cells[columnCount + 1 + i, 7] = sensorDeviceData.sensor_id;
                        worksheet.Cells[columnCount + 1 + i, 8] = sensorDeviceData.sensor_name;
                        worksheet.Cells[columnCount + 1 + i, 8] = sensorDeviceData.sensor_type;
                        worksheet.Cells[columnCount + 1 + i, 10] = sensorDeviceData.sensor_count;
                        worksheet.Cells[columnCount + 1 + i, 11] = sensorDeviceData.sensor_index;
                        worksheet.Cells[columnCount + 1 + i, 12] = sensorDeviceData.wave_length1;
                        worksheet.Cells[columnCount + 1 + i, 13] = sensorDeviceData.wave_length2;
                        worksheet.Cells[columnCount + 1 + i, 14] = sensorDeviceData.wave_length3;
                        worksheet.Cells[columnCount + 1 + i, 15] = sensorDeviceData.count_value1;
                        worksheet.Cells[columnCount + 1 + i, 16] = sensorDeviceData.count_value2;
                        worksheet.Cells[columnCount + 1 + i, 17] = sensorDeviceData.count_value3;
                    }

                    worksheet.Columns.EntireColumn.AutoFit();//列宽自适应
                }

                this.workbook.Saved = true;
                this.workbook.SaveAs(filePath);
            }
            catch (Exception ex)
            {
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    this.workbook.SaveAs(filePath);
                }
                else
                {
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    log.Error(ex.Message);
                    return;
                }
            }
        }
        
        private int CountColumnValues(Excel.Worksheet worksheet, int columnIndex)
        {
            int valueCount = 0;
            try
            {
                Excel.Range usedRange = worksheet.UsedRange;

                // 获取列的范围
                Excel.Range columnRange = (Excel.Range)worksheet.Columns[columnIndex];
                int lastRow = columnRange.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell).Row;

                // 遍历列中的所有单元格
                for (int row = 1; row <= lastRow; row++)
                {
                    Excel.Range cell = (Excel.Range)worksheet.Cells[row, columnIndex];
                    if (cell.Value2 != null && !cell.Value2.ToString().Trim().Equals(""))
                    {
                        valueCount++;
                    }
                }

                System.Runtime.InteropServices.Marshal.ReleaseComObject(columnRange);
                System.Runtime.InteropServices.Marshal.ReleaseComObject(usedRange);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            return valueCount;
        }


        private void CreateExcelDataFile()
        {
            if (!this.isSaveDataToExcel)
            {
                return;
            }
            this.excelApp = new Excel.Application();
            this.excelApp.Visible = false;
            this.excelApp.UserControl = true;
            this.excelApp.DisplayAlerts = false;

            DateTime dt = DateTime.Now;
            string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2") + dt.Hour.ToString("D2") + dt.Minute.ToString("D2") + dt.Second.ToString("D2");
            string filePath;
            if (waveFilePath == "")
            {
                filePath = AppDomain.CurrentDomain.BaseDirectory + waveFileName + @"-" + date + @".xlsx";
            }
            else
            {
                filePath = waveFilePath + waveFileName + @"-" + date + @".xlsx";
            }
            Excel.Workbooks workbooks = this.excelApp.Workbooks;

            try
            {
                if (!File.Exists(filePath))
                {
                    File.Copy(AppDomain.CurrentDomain.BaseDirectory + waveFileName + @".xlsx", filePath, true);
                }
                this.workbook = workbooks.Add(filePath); //加载模板
            }
            catch (Exception ex)
            {
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    this.workbook = workbooks.Add(filePath); //加载模板
                }
                else
                {
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    log.Error(ex.Message);
                    return;
                }
            }
            waveFileNameDate = date;
        }

        /*
        private void ReadExcelDataFile()
        {
            // 创建Excel应用程序实例
            this.excelApp = new Excel.Application();
            if (this.excelApp == null)
            {
                MessageBox.Show("无法创建excel对象，请确认excel环境是否正常");
                return;

            }
            this.excelApp.Visible = false;
            this.excelApp.UserControl = true;
            this.excelApp.DisplayAlerts = false;

            DateTime dt = DateTime.Today; // 获取今天的日期

            string date = dt.Year.ToString("D4") + dt.Month.ToString("D2") + dt.Day.ToString("D2") + dt.Hour.ToString("D2") + dt.Minute.ToString("D2") + dt.Second.ToString("D2");
            string filePath;
            if (waveFilePath == "")
            {
                filePath = AppDomain.CurrentDomain.BaseDirectory + waveFileName + @"-" + date + @".xlsx";
            }
            else
            {
                filePath = waveFilePath + waveFileName + @"-" + date + @".xlsx";
            }
            Excel.Workbooks workbooks = this.excelApp.Workbooks;
            try
            {
                if (File.Exists(filePath))
                {
                    this.workbook = workbooks.Add(filePath); //加载模板
                }
                else
                {
                    File.Copy(AppDomain.CurrentDomain.BaseDirectory + waveFileName + @".xlsx", filePath, true);
                    this.workbook = workbooks.Add(filePath); //加载模板
                }
            }
            catch (Exception ex)
            {
                bool isUnlocked = FileUnlocker.UnlockFile(filePath);
                if (isUnlocked)
                {
                    this.workbook = workbooks.Add(filePath); //加载模板
                }
                else
                {
                    MessageBox.Show("存储 Excel 文件时出错,文件可能正被打开！\n" + ex.Message);
                    log.Error(ex.ToString());
                    return;
                }
            }
        }
        */

        public static void SendEmail(string host, int port, string fromEmail, string password, string toEmail, string subject, string body)
        {
            try
            {
                // 创建邮件消息
                MailMessage mail = new MailMessage();
                mail.From = new MailAddress(fromEmail);
                mail.To.Add(toEmail);
                mail.Subject = subject;
                mail.Body = body;

                // 设置SMTP客户端
                using (SmtpClient client = new SmtpClient(host, port))
                {
                    client.Credentials = new NetworkCredential(fromEmail, password);
                    client.EnableSsl = true; // 如果SMTP服务器需要安全连接，则启用
                    // 发送邮件
                    client.Send(mail);
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        public static void SendSms(string phoneNumber, string[] templateParamSet)
        {
            try
            {
                // 必要步骤：
                // 实例化一个认证对象，入参需要传入腾讯云账户密钥对 SecretId，SecretKey。
                // 为了保护密钥安全，建议将密钥设置在环境变量中或者配置文件中。
                // 硬编码密钥到代码中有可能随代码泄露而暴露，有安全隐患，并不推荐。
                // 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
                // SecretId、SecretKey 查询：https://console.cloud.tencent.com/cam/capi
                Credential cred = new Credential
                {
                    //SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"),
                    //SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY")
                    SecretId = "AKIDaRMzSimmfMqDWfcQ1ShkutiNrAIFzXuA",
                    SecretKey = "CAyc6lCQ2cLypW2OaeowtfJqCOemkXBg"
                };

                /* 非必要步骤:
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                ClientProfile clientProfile = new ClientProfile();
                /* SDK默认用TC3-HMAC-SHA256进行签名
                 * 非必要请不要修改这个字段 */
                clientProfile.SignMethod = ClientProfile.SIGN_TC3SHA256;
                /* 非必要步骤
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                HttpProfile httpProfile = new HttpProfile();
                /* SDK默认使用POST方法。
                 * 如果您一定要使用GET方法，可以在这里设置。GET方法无法处理一些较大的请求 */
                httpProfile.ReqMethod = "POST";
                httpProfile.Timeout = 10; // 请求连接超时时间，单位为秒(默认60秒)
                /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
                httpProfile.Endpoint = "sms.tencentcloudapi.com";
                // 代理服务器，当您的环境下有代理服务器时设定（无需要直接忽略）
                // httpProfile.WebProxy = Environment.GetEnvironmentVariable("HTTPS_PROXY");

                clientProfile.HttpProfile = httpProfile;
                /* 实例化要请求产品(以sms为例)的client对象
                 * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
                SmsClient client = new SmsClient(cred, "ap-nanjing", clientProfile);

                /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
                 * 您可以直接查询SDK源码确定SendSmsRequest有哪些属性可以设置
                 * 属性可能是基本类型，也可能引用了另一个数据结构
                 * 推荐使用IDE进行开发，可以方便的跳转查阅各个接口和数据结构的文档说明 */
                SendSmsRequest req = new SendSmsRequest();

                /* 基本类型的设置:
                 * SDK采用的是指针风格指定参数，即使对于基本类型您也需要用指针来对参数赋值。
                 * SDK提供对基本类型的指针引用封装函数
                 * 帮助链接：
                 * 短信控制台: https://console.cloud.tencent.com/smsv2
                 * 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */
                /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666 */
                // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
                req.SmsSdkAppId = "1400957364";

                /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名 */
                // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
                req.SignName = "屏优多科技";

                /* 模板 ID: 必须填写已审核通过的模板 ID */
                // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
                req.TemplateId = "2342074";

                /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，若无模板参数，则设置为空 */
                req.TemplateParamSet = templateParamSet;

                /* 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]
                 * 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号*/
                req.PhoneNumberSet = new System.String[] { "+86" + phoneNumber };

                /* 用户的 session 内容（无需要可忽略）: 可以携带用户侧 ID 等上下文信息，tcpServerListener 会原样返回 */
                req.SessionContext = "告警";

                /* 短信码号扩展号（无需要可忽略）: 默认未开通，如需开通请联系 [腾讯云短信小助手] */
                req.ExtendCode = "";

                /* 国内短信无需填写该项；国际/港澳台短信已申请独立 SenderId 需要填写该字段，默认使用公共 SenderId，无需填写该字段。注：月度使用量达到指定量级可申请独立 SenderId 使用，详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
                req.SenderId = "";

                SendSmsResponse resp = client.SendSmsSync(req);

                // 输出json格式的字符串回包
                log.Info(AbstractModel.ToJsonString(resp));

                /* 当出现以下错误码时，快速解决方案参考
                 * [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
                 * 更多错误，可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
                 */
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
            //Console.Read();
        }

        public static void PullSmsSendStatus()
        {
            try
            {
                // 必要步骤：
                // 实例化一个认证对象，入参需要传入腾讯云账户密钥对 SecretId，SecretKey。
                // 为了保护密钥安全，建议将密钥设置在环境变量中或者配置文件中。
                // 硬编码密钥到代码中有可能随代码泄露而暴露，有安全隐患，并不推荐。
                // 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
                // SecretId、SecretKey 查询：https://console.cloud.tencent.com/cam/capi
                Credential cred = new Credential
                {
                    //SecretId = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_ID"),
                    //SecretKey = Environment.GetEnvironmentVariable("TENCENTCLOUD_SECRET_KEY")
                    SecretId = "AKIDaRMzSimmfMqDWfcQ1ShkutiNrAIFzXuA",
                    SecretKey = "CAyc6lCQ2cLypW2OaeowtfJqCOemkXBg"
                };
                /* 非必要步骤:
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                ClientProfile clientProfile = new ClientProfile();
                /* SDK 默认用 TC3-HMAC-SHA256 进行签名
                 * 非必要请不要修改该字段 */
                clientProfile.SignMethod = ClientProfile.SIGN_TC3SHA256;
                /* 非必要步骤
                 * 实例化一个客户端配置对象，可以指定超时时间等配置 */
                HttpProfile httpProfile = new HttpProfile();
                /* SDK 默认使用 POST 方法
                 * 如需使用 GET 方法，可以在此处设置，但 GET 方法无法处理较大的请求 */
                httpProfile.ReqMethod = "POST";
                httpProfile.Timeout = 30; // 请求连接超时时间，单位为秒(默认60秒)
                /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
                httpProfile.Endpoint = "sms.tencentcloudapi.com";
                // 代理服务器，当您的环境下有代理服务器时设定
                // httpProfile.WebProxy = Environment.GetEnvironmentVariable("HTTPS_PROXY");
                clientProfile.HttpProfile = httpProfile;
                /* 实例化 SMS 的 client 对象
                 * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
                SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);
                /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数
                 * 您可以直接查询 SDK 源码确定 SendSmsRequest 有哪些属性可以设置
                 * 属性可能是基本类型，也可能引用了另一个数据结构
                 * 推荐使用 IDE 进行开发，可以方便地跳转查阅各个接口和数据结构的文档说明 */
                PullSmsSendStatusRequest req = new PullSmsSendStatusRequest();

                /* 基本类型的设置:
                 * SDK 采用的是指针风格指定参数，即使对于基本类型也需要用指针来对参数赋值
                 * SDK 提供对基本类型的指针引用封装函数
                 * 帮助链接：
                 * 短信控制台：https://console.cloud.tencent.com/smsv2
                 * 腾讯云短信小助手：https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81 */

                // 设置拉取最大条数，最多100条
                req.Limit = 100;
                /* 短信应用 ID: 在 [短信控制台] 添加应用后生成的实际 SdkAppId，例如1400006666 */
                req.SmsSdkAppId = "1400957364";
                // 通过 client 对象调用 PullSmsSendStatus 方法发起请求，注意请求方法名与请求对象是对应的
                // 返回的 resp 是一个 PullSmsSendStatusResponse 类的实例，与请求对象对应
                PullSmsSendStatusResponse resp = client.PullSmsSendStatusSync(req);
                // 输出 JSON 格式的字符串回包
                log.Info(AbstractModel.ToJsonString(resp));
            }
            catch (Exception ex)
            {
                log.Error( ex.Message);
            }
            //Console.Read();
        }

        private void cbWaveSensorType_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                this.sensorType = cbWaveSensorType.SelectedIndex;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbWaveSensorType"].Value = this.cbWaveSensorType.SelectedIndex + "";
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void cbWaveChannelSellect_SelectedIndexChanged(object sender, EventArgs e)
        {
            // 假设你在非UI线程上
            if (this.cbWaveChannelSellect.InvokeRequired)
            {
                this.cbWaveChannelSellect.Invoke(new System.Action(() =>
                {
                    if (this.channelIndex != this.cbWaveChannelSellect.SelectedIndex)
                    {
                        this.channelIndex = this.cbWaveChannelSellect.SelectedIndex;
                        try
                        {
                            InitChartConfig();
                        }
                        catch (Exception ex)
                        {
                            log.Error(ex.Message);
                        }
                        finally
                        {
                        }
                    }
                }));
            }
            else
            {
                // 直接进行操作，因为当前线程就是UI线程
                if (this.channelIndex != this.cbWaveChannelSellect.SelectedIndex)
                {
                    this.channelIndex = this.cbWaveChannelSellect.SelectedIndex;
                    try
                    {
                        InitChartConfig();
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex.Message);
                    }
                    finally
                    {
                    }
                }
            }
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbWaveChannelSellect"].Value = this.cbWaveChannelSellect.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void cbWaveSensorSellect_SelectedIndexChanged(object sender, EventArgs e)
        {
            // 假设你在非UI线程上
            if (this.cbWaveSensorSellect.InvokeRequired)
            {
                this.cbWaveSensorSellect.Invoke(new System.Action(() =>
                {
                    if (this.rasterIndex != this.cbWaveSensorSellect.SelectedIndex)
                    {
                        this.rasterIndex = this.cbWaveSensorSellect.SelectedIndex;
                        try
                        {
                            InitChartConfig();
                        }
                        catch (Exception ex)
                        {
                            log.Error(ex.Message);
                        }
                        finally
                        {
                        }
                    }
                }));
            }
            else
            {
                // 直接进行操作，因为当前线程就是UI线程
                if (this.rasterIndex != this.cbWaveSensorSellect.SelectedIndex)
                {
                    this.rasterIndex = this.cbWaveSensorSellect.SelectedIndex;
                    try
                    {
                        InitChartConfig();
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex.Message);
                    }
                    finally
                    {
                    }
                }
            }
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbWaveSensorSellect"].Value = this.cbWaveSensorSellect.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void tbWaveFrequency_TextChanged(object sender, EventArgs e)
        {
            try
            {
                if (this.tbWaveFrequency.Text.Length > 0)
                {
                    long _frequency = Convert.ToInt64(this.tbWaveFrequency.Text);
                    //转换成长整型
                    if (this.frequency != _frequency)
                    {
                        this.frequency = _frequency;
                        this.intervalTime = 1000 / this.frequency;
                        this.xCount = this.showTimes * this.frequency;
                    }
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    AppSettingsSection app = config.AppSettings;
                    app.Settings["tbWaveFrequency"].Value = this.tbWaveFrequency.Text;
                    config.Save(ConfigurationSaveMode.Modified);
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void cbSaveWaveExcel_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                this.isSaveDataToExcel = this.cbSaveWaveExcel.Checked;
                this.tbWaveFilePath.Enabled = this.cbSaveWaveExcel.Checked;
                this.ibOpenFilePath.Enabled = this.cbSaveWaveExcel.Checked;

                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbSaveWaveExcel"].Value = this.cbSaveWaveExcel.Checked.ToString();
                config.Save(ConfigurationSaveMode.Modified);

                if (this.isSaveDataToExcel)
                {
                    CreateExcelDataFile();
                }
                else
                {
                    if (excelApp != null)
                    {
                        excelApp.DisplayAlerts = false;
                        if (workbook != null)
                        {
                            workbook.Close(false);
                            workbook = null;
                        }
                        excelApp.Quit();
                        excelApp = null;
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void tbWaveFilePath_TextChanged(object sender, EventArgs e)
        {
            try
            {
                if (this.tbWaveFilePath.Text.Length > 0)
                {
                    if (!this.waveFilePath.Equals(this.tbWaveFilePath.Text))
                    {
                        this.waveFilePath = this.tbWaveFilePath.Text;
                        if (this.isSaveDataToExcel)
                        {
                            if (this.excelApp != null)
                            {
                                this.excelApp.DisplayAlerts = false;
                                if (this.workbook != null)
                                {
                                    this.workbook.Close(false);
                                    this.workbook = null;
                                }
                                this.excelApp.Quit();
                                this.excelApp = null;
                            }
                            CreateExcelDataFile();
                        }
                        Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                        AppSettingsSection app = config.AppSettings;
                        app.Settings["tbWaveFilePath"].Value = this.tbWaveFilePath.Text;
                        config.Save(ConfigurationSaveMode.Modified);
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void cbWaveCollectTimes_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                this.tbWaveCollectTimes.Enabled = this.cbWaveCollectTimes.Checked;
                this.isWaveCollectTimes = this.cbWaveCollectTimes.Checked;
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbWaveCollectTimes"].Value = this.cbWaveCollectTimes.Checked.ToString();
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void tbWaveCollectTimes_TextChanged(object sender, EventArgs e)
        {
            try
            {
                if (this.tbWaveCollectTimes.Text.Length > 0)
                {
                    this.waveCollectTimes = Convert.ToInt32(this.tbWaveCollectTimes.Text);
                    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                    AppSettingsSection app = config.AppSettings;
                    app.Settings["tbWaveCollectTimes"].Value = this.tbWaveCollectTimes.Text;
                    config.Save(ConfigurationSaveMode.Modified);
                }
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void ibOpenFilePath_Click(object sender, EventArgs e)
        {
            try
            {
                FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
                folderBrowserDialog.ShowDialog();
                this.tbWaveFilePath.Text = folderBrowserDialog.SelectedPath; //获得选择的文件夹路径
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        public static bool getIsAlarmNotify()
        {
            return isAlarmNotify;
        }

        public static void setIsAlarmNotify(bool param)
        {
            isAlarmNotify = param;
        }

        public static double getAxialForceExceeds()
        {
            return axialForceExceeds;
        }

        public static void setAxialForceExceeds(double param)
        {
            axialForceExceeds = param;
        }

        public static int getRepeatTimes()
        {
            return repeatTimes;
        }

        public static void setRepeatTimes(int param)
        {
            repeatTimes = param;
        }

        public static bool getIsAlarmPhone()
        {
            return isAlarmPhone;
        }

        public static void setIsAlarmPhone(bool param)
        {
            isAlarmPhone = param;
        }

        public static string getAlarmPhone()
        {
            return alarmPhone;
        }

        public static void setAlarmPhone(string param)
        {
            alarmPhone = param;
        }

        public static bool getIsAlarmEmail()
        {
            return isAlarmEmail;
        }

        public static void setIsAlarmEmail(bool param)
        {
            isAlarmEmail = param;
        }

        public static string getAlarmEmail()
        {
            return alarmEmail;
        }

        public static void setAlarmEmail(string param)
        {
            alarmEmail = param;
        }

        public static double getTempStartWave()
        {
            return tempStartWave;
        }

        public static void setTempStartWave(double param)
        {
            tempStartWave = param;
        }

        public static double getTempStartValue()
        {
            return tempStartValue;
        }

        public static void setTempStartValue(double param)
        {
            tempStartValue = param;
        }

        public static Socket getServerSocket()
        {
            return serverSocket;
        }

        public static EndPoint getEndPoint()
        {
            return adEndPoint;
        }

        public static bool getOpenServerPort()
        {
            return isOpenServerPort;
        }

        //根据字符串获取端口
        public static bool IsDomain(string str)
        {
            string pattern = @"^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$";
            return IsMatch(pattern, str);
        }

        // 验证字符串是否是IP
        public static bool IsIP(string str)
        {
            string pattern = @"(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)";
            return IsMatch(pattern, str);
        }

        //判断是否为0-65535的数字
        public static bool IsPort(string str)
        {
            string pattern = @"^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$";
            return IsMatch(pattern, str);
        }

        // 判断一个字符串，是否匹配指定的表达式(区分大小写的情况下)
        public static bool IsMatch(string expression, string str)
        {
            Regex reg = new Regex(expression);
            if (string.IsNullOrEmpty(str))
            {
                return false;
            }
            return reg.IsMatch(str);
        }

        public static int getPort(string str)
        {
            if (!IsPort(str))
            {
                //不是端口
                return -1;
            }
            //转换成整型
            int port = Convert.ToInt32(str);
            if (port >= 0 && port <= 65535)
            {
                //如果是合法端口
                return port;
            }
            else
            {//不是合法端口
                return -1;
            }
        }

        //根据字符串获取IP地址
        public static string getIPAddress(string str)
        {
            if (!IsIP(str))
            {
                //如果不是IP
                if (IsDomain(str))
                {
                    //如果是域名
                    try
                    {
                        //解析域名
                        IPAddress[] host = Dns.GetHostAddresses(str);
                        //域名解析的IP地址
                        IPAddress ipAddress = host[0];
                        //改成IP地址
                        str = ipAddress.ToString();
                    }
                    catch
                    {
                        return "";
                    }
                }
                else
                {
                    return "";
                }
            }
            //返回IP地址
            return str;
        }

        private void cbMyAddress_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbMyAddress"].Value = cbMyAddress.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void tbMyPort_TextChanged(object sender, EventArgs e)
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbMyPort"].Value = tbMyPort.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        public static long ToUnixTimestamp(DateTime dateTime)
        {
            return (long)Math.Floor((dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds);
        }

        public static long ToUnixTimestampMilliseconds(DateTime dateTime)
        {
            return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }

        private void cbMyAddress_SelectedIndexChanged_1(object sender, EventArgs e)
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["cbMyAddress"].Value = cbMyAddress.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        private void tbMyPort_TextChanged_1(object sender, EventArgs e)
        {
            try
            {
                Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                AppSettingsSection app = config.AppSettings;
                app.Settings["tbMyPort"].Value = tbMyPort.Text;
                config.Save(ConfigurationSaveMode.Modified);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        public void RecordUserOperation(string operation)
        {
            try
            {
                string userName = LoginForm.username;
                string userIp = "127.0.0.1";
                long recordTime = ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeMilliseconds();
                MainForm.db.ExecuteSql(
                    MainForm.db.Insert("system_operation_content")
                        .SetColumn("UserName", userName)
                        .SetColumn("UserIP", userIp)
                        .SetColumn("RecordTime", recordTime)
                        .SetColumn("Operation", operation)
                        .ToSql());
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }

        public void RecordAlarmNotice(SensorDeviceData sensorDeviceData, string notice, string email, string sms)
        {
            try
            {
                MainForm.db.ExecuteSql(
                    MainForm.db.Insert("sensor_alarm_notice")
                        .SetColumn("AlarmTime", sensorDeviceData.collect_time)
                        .SetColumn("AlarmDateTime", sensorDeviceData.collect_datetime)
                        .SetColumn("SensorID", sensorDeviceData.sensor_id)
                        .SetColumn("SensorName", sensorDeviceData.sensor_name)
                        .SetColumn("ChannelIndex", sensorDeviceData.channel_id)
                        .SetColumn("SensorIndex", sensorDeviceData.sensor_index)
                        .SetColumn("WaveLength", sensorDeviceData.wave_length1)
                        .SetColumn("Temperature", sensorDeviceData.count_value1)
                        .SetColumn("Description", notice)
                        .SetColumn("Email", email)
                        .SetColumn("SMS", sms)
                        .ToSql());
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
            }
        }
    }


}


public class SensorDeviceData
{
    //采集时间
    public string collect_datetime { get; set; }
    //采集时间
    public long collect_time { get; set; }
    //通道ID
    public int channel_id { get; set; }
    //传感器类型
    public int sensor_type { get; set; }
    //传感器数量
    public int sensor_count { get; set; }
    //传感器索引
    public int sensor_index { get; set; }
    //传感器ID
    public string sensor_id { get; set; }
    //传感器名称
    public string sensor_name { get; set; }
    //光栅波长1
    public double wave_length1 { get; set; }
    //光栅波长2
    public double wave_length2 { get; set; }
    //光栅波长3
    public double wave_length3 { get; set; }
    //结果1
    public double count_value1 { get; set; }
    //结果2
    public double count_value2 { get; set; }
    //结果3
    public double count_value3 { get; set; }

    public override string ToString()
    {
        return collect_datetime + ", " + collect_time + ", " + channel_id + ", " + sensor_type + ", " + sensor_count + ", " + sensor_index + ", " + sensor_id + ", " + sensor_name + ", " + wave_length1 + ", " + wave_length2 + ", " + wave_length3 + ", " + count_value1 + ", " + count_value2 + ", " + count_value3;
    }
}